# -*- coding: UTF-8 -*-
import tkinter as tk
from Code.View.sc_global import Global
from Code.View.sc_base import PageBase, wrapper_exception, PageDataError, NotInputError
from Code.View.sc_module import TitleFrame, ScrollFrame
from Code.View.sc_widgets import WidgetsTemplate
from Code.View.sc_timezone import TimezoneWidgets
from Code.View.Datamation.sc_provide import view_gate

language_data = view_gate.query_language_data


class PageCreator(PageBase):
    def __init__(self, master, title, width, height, data):
        self.master = master
        self.title = title
        self.width = width
        self.height = height
        self.page_data = data
        self.ip_list = view_gate.query_login_ip_data()
        self.widgets_instance = []       # 保存页面所有控件实例及其属性
        self.plot_widgets = []           # 保存页面图表控件实例及其属性
        self.pager = self

    def pack_frame(self):
        """
        自定义界面类时, 类名为Trees.Page.Widgets的值,同时需要自己实现以下方法:
        pack_widgets()            # 布局控件
        get_plot_widgets()        # 获取所有图表控件实例
        clear_widgets_values()    # 清理所有可数据回填控件的值
        get_widgets_values()      # 获取所有控件的值
        update_widgets_values()   # 更新所有控件的值
        """
        if Attribution.is_custom_page:
            self.pager = eval(self.page_data['Widgets'])(self.frame, self.width, self.height)
        self.pager.pack_widgets()

    @classmethod
    def check_widget_type(cls, types, attrs, actions):
        not_support = language_data('NOT_SUPPORT')
        if types not in Global.G_SUPPORTED_WIDGETS:
            raise PageDataError("WidgetType: {} {}".format(types, not_support))
        if attrs.has_markdown_format and types not in ['InfoText', 'MultiInfoText']:
            raise PageDataError("{} {} 'Markdown'".format(types, not_support))
        if actions.has_upload_file and types not in ['FileButton']:
            raise PageDataError("{} {} 'UploadFile'".format(types, not_support))

    @classmethod
    def check_plot_widget(cls, types):
        return types in Global.G_ALL_PLOT_WIDGETS

    def get_widget_params(self, types, params):
        top_w, top_h, widget_w, widget_h = params['Size']
        # 这两个共页复合控件特殊处理一下高度, 需要动态调整
        if types in ['MultiCombobox', 'MultiScaleBar']:
            count = len(self.ip_list)
            limit = Global.G_MULTI_COLUMN_LIMIT
            top_h = (count // limit + (1 if count % limit else 0)) * 55
        return (top_w, top_h), (widget_w, widget_h)

    def parse_widget_values(self, types, values):
        def trans_value():
            trans = []
            for val in values:
                real = [val]
                if val == '__LOGIN_IPS__':
                    real = self.ip_list
                trans += real
            return trans

        if types.startswith('Multi'):
            trans_values = {}
            for ip in self.ip_list:
                trans_values[ip] = trans_value()
        else:
            trans_values = trans_value()
        return trans_values

    def create_widget(self, types, values, master, size):
        return WidgetsTemplate.create_widget(types, master, size[0], size[1], values)

    @wrapper_exception
    def pack_widgets(self):
        try:
            widgets = view_gate.query_widgets_data()[self.page_data['Widgets']]
            for index, widget in enumerate(widgets):
                # 解析控件数据
                widget_type = widget['WidgetType']
                widget_tips = widget['WidgetTips']
                widget_values = widget['WidgetValues']
                widget_params = widget['WidgetParams']
                widget_attrs = set(widget['WidgetAttrs'])
                widget_actions = set(widget['WidgetActions'])
                # 属性和动作单独实现
                attrs = Attribution.WidgetAttrs(widget_attrs)
                actions = Action.WidgetAction(widget_actions)
                self.check_widget_type(widget_type, attrs, actions)
                master_size, widget_size = self.get_widget_params(widget_type, widget_params)
                widget_values = self.parse_widget_values(widget_type, widget_values)
                master = TitleFrame(self.frame, master_size[0], master_size[1], '\n'.join(widget_tips)).master()
                widget_instance = self.create_widget(widget_type, widget_values, master, widget_size)
                if self.check_plot_widget(widget_type):
                    self.plot_widgets.append(widget_instance)
                self.widgets_instance.append((widget_type, widget_instance, attrs, actions))
        except KeyError as e:
            raise PageDataError(str(e))

    def get_plot_widgets(self):
        if self.pager != self:
            return self.pager.get_plot_widgets()
        return self.plot_widgets

    def clear_widgets_values(self):
        if self.pager != self:
            self.pager.clear_widgets_values()
            return
        for types, instance, _, _ in self.widgets_instance:
            if types in ['InfoText']:     # Global.G_SETABLE_WIDGETS:
                WidgetsTemplate.clear_value(instance)

    def get_widgets_values(self, ip):
        if self.pager != self:
            values, uploads = self.pager.get_widgets_values(ip)
            Action.upload_files.update(uploads)
            return values
        values = []
        Action.upload_files[ip] = []
        for types, instance, attrs, actions in self.widgets_instance:
            if types in Global.G_READONLY_WIDGETS:
                values.append({})
                continue
            value = WidgetsTemplate.get_value(instance, ip)
            if not attrs.has_permit_empty:
                """ 控件数据不可为空的判断, 控件返回类型目前只有str和list两种 """
                if (isinstance(value, str) and not value) or (isinstance(value, list) and not any(value)):
                    raise NotInputError("{} {}".format(types, language_data('CANT_NULL')))
            if actions.has_upload_file:
                Action.upload_files[ip].append(value)
            values.append({types: value})
        return values

    def update_widgets_values(self, ip, values):
        if self.pager != self:
            return self.pager.update_widgets_values(ip, values)
        for index, items in enumerate(self.widgets_instance):
            types, instance, attrs, _ = items
            if (not values[index]) or types not in Global.G_SETABLE_WIDGETS:
                continue
            (which, value), = values[index].items()
            if which != types:
                raise PageDataError("No.{} WidgetType: {}, Not {}".format(index, types, which))
            WidgetsTemplate.set_value(instance, value, ip, attrs.has_markdown_format)

    def update_plot_widgets(self, instance, ip, image):
        WidgetsTemplate.set_value(instance, image, ip)

    def append_widgets_tab(self, ip):
        for types, instance, _, _ in self.widgets_instance:
            if types.startswith('Multi'):
                WidgetsTemplate.add_tab(instance, ip)
            if types in Global.G_ALL_PLOT_WIDGETS:
                for items in self.plot_widgets:
                    inst, values, _ = items
                    if inst == instance:
                        value = [values[list(values.keys())[0]][-1]]
                        values[ip] = value
                        break

    def remove_widgets_tab(self, ip):
        for types, instance, _, _ in self.widgets_instance:
            if types.startswith('Multi'):
                WidgetsTemplate.remove_tab(instance, ip)


class Attribution:
    has_no_start_button = False
    has_be_run_in_root = False
    has_not_run_in_root = False
    has_toolbar_icon = False
    has_pre_handle = False
    is_custom_page = False

    @classmethod
    def _check_attrs(cls, types, supports, attrs):
        start_is = []
        for attr in attrs:
            if attr not in supports:
                raise PageDataError("{}Attrs: {} {}".format(types, attrs, language_data('NOT_SUPPORT')))
            if str(attr).startswith('Is'):
                start_is.append(attr)
        if len(start_is) > 1:
            raise PageDataError("{}Attrs: {} {}".format(types, start_is, language_data('CONFLICT')))

    @classmethod
    def parse_page_attrs(cls, attrs, check=True):
        cls.has_no_start_button = 'HasNoStartButtons' in attrs
        cls.has_be_run_in_root = 'HasBeRunInRoot' in attrs
        cls.has_not_run_in_root = 'HasNotRunInRoot' in attrs
        cls.has_toolbar_icon = 'HasToolBarIcon' in attrs
        cls.has_pre_handle = 'HasPreHandle' in attrs
        cls.is_custom_page = 'IsCustomPage' in attrs
        if check:
            if cls.has_be_run_in_root and cls.has_not_run_in_root:
                raise PageDataError("PageAttrs: HasBeRunInRoot & HasNotRunInRoot {}".format(language_data('CONFLICT')))
            page_attrs = ['HasNoStartButtons', 'HasBeRunInRoot', 'HasNotRunInRoot', 'HasPreHandle',
                          'HasToolBarIcon', 'IsCustomPage']
            cls._check_attrs('Page', page_attrs, attrs)

    class WidgetAttrs(object):
        def __init__(self, attrs):
            widget_attrs = ['HasPermitEmpty', 'HasMarkdownFormat']
            Attribution._check_attrs('Widget', widget_attrs, attrs)
            self.has_permit_empty = 'HasPermitEmpty' in attrs
            self.has_markdown_format = 'HasMarkdownFormat' in attrs


class Action:
    upload_files = {}

    @classmethod
    def _check_actions(cls, supports, actions):
        for act in actions:
            if act not in supports:
                raise PageDataError("WidgetActions: {} {}".format(act, language_data('NOT_SUPPORT')))

    class WidgetAction(object):
        def __init__(self, actions):
            widget_actions = ["UploadFile"]
            Action._check_actions(widget_actions, actions)
            self.has_upload_file = 'UploadFile' in actions


class PageCtrl(object):
    """ 页面切换控制类 """

    def __init__(self, master, pages_data):
        self.master = master
        self.pages_data = pages_data
        self.width = Global.G_MAIN_PAGE_WIDTH
        self.height = Global.G_MAIN_OPER_HEIGHT
        self.curr_page = None
        self.page_instance = None

    def switch_default(self):
        master = tk.Frame(self.master)
        master.pack()
        self.page_instance = master
        frame = ScrollFrame(master, self.width, self.height).master()
        tk.Label(frame, image=view_gate.query_photo_image_data('DEFAULT')).pack()

    def destroy_page(self):
        try:
            self.page_instance.destroy()
        except:
            pass

    def is_alive(self):
        return self.page_instance.alive()

    @wrapper_exception
    def switch_page(self, page_name):
        self.destroy_page()
        data = self.pages_data[page_name]
        Attribution.parse_page_attrs(data['Attrs'])
        self.page_instance = PageCreator(self.master, page_name, self.width, self.height, data)
        self.page_instance.pack()
        self.curr_page = page_name
        return True

    def get_page_script(self):
        return self.pages_data[self.curr_page]['Script']

    def get_toolbar_icons(self):
        members = []
        for page_name, data in self.pages_data.items():
            Attribution.parse_page_attrs(data['Attrs'], check=False)
            if Attribution.has_toolbar_icon:
                members.append((page_name, data['Image'], 0))
        return members

    def get_plot_widgets(self):
        return self.page_instance.get_plot_widgets()

    def clear_widgets_values(self):
        self.page_instance.clear_widgets_values()

    def get_widgets_values(self, ip):
        return self.page_instance.get_widgets_values(ip)

    def update_widgets_values(self, ip, values):
        self.page_instance.update_widgets_values(ip, values)

    def update_plot_widgets(self, instance, ip, image):
        self.page_instance.update_plot_widgets(instance, ip, image)

    def append_widgets_tab(self, ip):
        if isinstance(self.page_instance, PageCreator):
            self.page_instance.append_widgets_tab(ip)

    def remove_widgets_tab(self, ip):
        self.page_instance.remove_widgets_tab(ip)

